package net.myspring.common.mybatis.provider;

import com.google.common.collect.Lists;
import net.myspring.common.mybatis.interceptor.MybatisPageableInterceptor;
import net.myspring.common.mybatis.provider.annotation.GenerationType;
import net.myspring.common.mybatis.provider.model.MybatisColumn;
import net.myspring.common.mybatis.provider.model.MybatisTable;
import net.myspring.common.utils.ReflectionUtils;
import net.myspring.common.utils.StringUtils;
import net.myspring.common.utils.ThreadLocalContext;
import  org.apache.ibatis.jdbc.SQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;

/**
 * Created by liuj on 2016/11/12.
 */
public class CrudProvider extends BaseProvider {
    private static Logger logger = LoggerFactory.getLogger(CrudProvider.class);
    public static final String SAVE = "save";
    public static final String UPDATE = "update";
    public static final String BATCH_SAVE = "batchSave";
    public static final String FIND_ALL = "findAll";
    public static final String FIND_ONE = "findOne";
    public static final String FIND_BY_IDS = "findByIds";
    public static final String COUNT = "count";
    public static final String DELETE_ONE = "deleteOne";
    public static final String DELETE_BY_IDS = "deleteByIds";
    public static final String LOGIC_DELETE_ONE = "logicDeleteOne";
    public static final String LOGIC_DELETE_BY_IDS = "logicDeleteByIds";

    public String save(Object entity) {
        List<String> columnNames = Lists.newArrayList();
        List<String> fieldNames = Lists.newArrayList();
        for(MybatisColumn mybatisColumn:getMybatisTable().getMybatisColumnList()) {
            boolean insertable = getInsertable(getMybatisTable(),mybatisColumn);
            if(insertable) {
                columnNames.add(mybatisColumn.getName());
                fieldNames.add("#{" + mybatisColumn.getFieldName() + "}");
            }
        }
        StringBuilder sb = new StringBuilder("INSERT INTO ");
        sb.append(getMybatisTable().getName());
        sb.append(" (");
        sb.append(StringUtils.join(columnNames,","));
        sb.append(") ");
        sb.append(" VALUES(");
        sb.append(StringUtils.join(fieldNames,","));
        sb.append(")");
        String sql = sb.toString();
        logger.info(sql);
        return sql;
    }


    public String update(Object entity) {
        List<String>  updateColumns = Lists.newArrayList();
        for(MybatisColumn mybatisColumn:getMybatisTable().getMybatisColumnList()) {
            boolean updateable = mybatisColumn.isUpdatable();
            if(getMybatisTable().getId() != null && getMybatisTable().getId().getName().equals(mybatisColumn.getName())) {
                updateable = false;
            } else if(getMybatisTable().getVersion() != null && getMybatisTable().getVersion().getName().equals(mybatisColumn.getName())) {
                updateable = false;
            } else if(getMybatisTable().getCreatedBy() != null && getMybatisTable().getCreatedBy().getName().equals(mybatisColumn.getName())) {
                updateable = false;
            }else if(getMybatisTable().getCreatedDate() != null && getMybatisTable().getCreatedDate().getName().equals(mybatisColumn.getName())) {
                updateable = false;
            } else if(getMybatisTable().getLastModifiedBy() != null && getMybatisTable().getLastModifiedBy().getName().equals(mybatisColumn.getName())) {
                updateable = true;
            }else if(getMybatisTable().getLastModifiedDate() != null && getMybatisTable().getLastModifiedDate().getName().equals(mybatisColumn.getName())) {
                updateable = true;
            } else {
                if(ReflectionUtils.getFieldValue(entity,mybatisColumn.getFieldName())==null) {
                    updateable = false;
                }
            }
            if(updateable) {
                updateColumns.add(mybatisColumn.getName() + " = " + "#{" + mybatisColumn.getFieldName() + "}");
            }
        }
        StringBuilder sb = new StringBuilder("UPDATE ");
        sb.append(getMybatisTable().getName());
        sb.append(" SET ");
        sb.append(StringUtils.join(updateColumns,","));
        sb.append(" WHERE ");
        sb.append(getMybatisTable().getId().getName());
        sb.append(" = ");
        sb.append("#{" + getMybatisTable().getId().getFieldName() + "}");
        String sql = sb.toString();
        logger.info(sql);
        return sql;
    }

    public String batchSave(Map map) {
        List<Object> list = (List<Object>) map.get("list");
        List<String> columnNames = Lists.newArrayList();
        List<String> fieldNames = Lists.newArrayList();
        List<String> insertValues = Lists.newArrayList();
        MybatisTable mybatisTable = getMybatisTable();
        for(MybatisColumn mybatisColumn:mybatisTable.getMybatisColumnList()) {
            boolean insertable = getInsertable(getMybatisTable(),mybatisColumn);
            if(insertable) {
                columnNames.add(mybatisColumn.getName());
                fieldNames.add(mybatisColumn.getFieldName());
            }
        }
        StringBuilder sb = new StringBuilder("INSERT INTO ");
        sb.append(getMybatisTable().getName());
        sb.append(" (");
        sb.append(StringUtils.join(columnNames,","));
        sb.append(") ");
        sb.append(" VALUES");
        for(int i=0;i<list.size();i++) {
            List<String> insertItem = Lists.newArrayList();
            StringBuilder insertValue = new StringBuilder("(");
            for(String fieldName:fieldNames) {
                insertItem.add("#{list[" +  i + "]." + fieldName + "}");
            }
            insertValue.append(StringUtils.join(insertItem,","));
            insertValue.append(")");
            insertValues.add(insertValue.toString());
        }
        sb.append(StringUtils.join(insertValues,","));
        String sql = sb.toString();
        logger.info(sql);
        return sql;
    }

    public String findAll() {
        String sql = "SELECT * FROM " + getMybatisTable().getName();
        logger.info(sql);
        return sql;
    }

    public String findOne(Object id) {
        String sql = "SELECT * FROM " + getMybatisTable().getName() + " WHERE " + getMybatisTable().getId().getName() + "=#{id}";
        logger.info(sql);
        return sql;
    }

    public String findByIds(Map map) {
        List<Object> list = (List<Object>) map.get("list");
        List<String> values = Lists.newArrayList();
        for(int i =0;i<list.size();i++) {
            values.add("#{list[" + i + "]}");
        }
        String sql = "SELECT * FROM " + getMybatisTable().getName() + " WHERE " + getMybatisTable().getId().getName() + " IN (" + StringUtils.join(values,",") + ")";
        logger.info(sql);
        return sql;
    }

    public String count() {
        String sql = "SELECT COUNT(" + getMybatisTable().getId().getName() + ") FROM " + getMybatisTable().getName();
        logger.info(sql);
        return sql;
    }

    public String deleteOne(Object id) {
        String sql =  "DELETE FROM " + getMybatisTable().getName() + " WHERE " + getMybatisTable().getId().getName() + "=#{id}";
        logger.info(sql);
        return sql;
    }

    public String deleteByIds(Map map) {
        List<Object> list = (List<Object>) map.get("list");
        List<String> values = Lists.newArrayList();
        for(int i =0;i<list.size();i++) {
            values.add("#{list[" + i + "]}");
        }
        String sql = "DELETE FROM " + getMybatisTable().getName() + " WHERE " + getMybatisTable().getId().getName() + " IN (" + StringUtils.join(values,",") + ")";
        logger.info(sql);
        return sql;
    }


    public String logicDeleteOne(Object id) {
        String sql =  "UPDATE " + getMybatisTable().getName() + "SET "  + getMybatisTable().getEnabled().getName() + "=0 WHERE " + getMybatisTable().getId().getName() + "=#{id}";
        logger.info(sql);
        return sql;
    }

    public String logicDeleteByIds(Map map) {
        List<Object> list = (List<Object>) map.get("list");
        List<String> values = Lists.newArrayList();
        for(int i =0;i<list.size();i++) {
            values.add("#{list[" + i + "]}");
        }
        String sql = "UPDATE " + getMybatisTable().getName() + "SET "  + getMybatisTable().getEnabled().getName() + "=0 WHERE " + getMybatisTable().getId().getName() + " IN (" + StringUtils.join(values,",") + ")";
        logger.info(sql);
        return sql;
    }



    private  boolean getInsertable(MybatisTable mybatisTable,MybatisColumn mybatisColumn) {
        boolean insertable = mybatisColumn.isInsertable();
        if(getMybatisTable().getId() != null && getMybatisTable().getId().getName().equals(mybatisColumn.getName())) {
            if (GenerationType.IDENTITY.name().equals(getMybatisTable().getGenerationType())) {
                insertable = false;
            } else {
                insertable = true;
            }
        } else if(getMybatisTable().getVersion() != null && getMybatisTable().getVersion().getName().equals(mybatisColumn.getName())) {
            insertable = true;
        } else if(getMybatisTable().getCreatedBy() != null && getMybatisTable().getCreatedBy().getName().equals(mybatisColumn.getName())) {
            insertable = true;
        }else if(getMybatisTable().getCreatedDate() != null && getMybatisTable().getCreatedDate().getName().equals(mybatisColumn.getName())) {
            insertable = true;
        } else if(getMybatisTable().getLastModifiedBy() != null && getMybatisTable().getLastModifiedBy().getName().equals(mybatisColumn.getName())) {
            insertable = true;
        }else if(getMybatisTable().getLastModifiedDate() != null && getMybatisTable().getLastModifiedDate().getName().equals(mybatisColumn.getName())) {
            insertable = true;
        }
        return insertable;
    }


}